/*
 * Copyright (c) 2009, Hamish Morgan. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * 3. Neither the name of the University of Sussex nor the names of its
 *    contributors may be used to endorse or promote products derived from
 *    this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */
package locusts.server.admin;

import javax.naming.OperationNotSupportedException;
import javax.swing.table.AbstractTableModel;
import locusts.common.Player;
import locusts.common.entities.EntityData;
import locusts.common.entities.EntityDataList;
import locusts.common.entities.EntityType;
import locusts.common.entities.EntityTypeList;
import locusts.server.Game;
import locusts.server.GameListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 *
 * @author Hamish Morgan
 */
public class TypeDataTableModel extends AbstractTableModel {

    private static final Logger LOG = LoggerFactory.getLogger(
            TypeDataTableModel.class);
//    //
    private final Column idColumn = new IdColumn();
    private final Column nameColumn = new NameColumn();
    private final Column maxEnergyColumn = new MaxEnergyColumn();
    private final Column minEnergyColumn = new MinEnergyColumn();
    private final Column dieOnZeroColumn = new DieOnMinEnergyColumn();
    private final Column energyDepletionColumn =
            new EnergyDepletionRateColumn();
    private final Column massColumn = new MassColumn();
    private final EntityDataList data;
    private final EntityTypeList type;
    private final Column[] columnOrder = new Column[]{nameColumn, idColumn,
        minEnergyColumn, maxEnergyColumn, dieOnZeroColumn,
        energyDepletionColumn, massColumn};
    private long lastUpdate = 0;
    private long updateRate = 1000;

    public TypeDataTableModel(Game game) {
        data = game.getEntityData();
        type = game.getEntityTypes();
        game.addGameListener(new TMGameListener());
    }

    private class TMGameListener implements GameListener {

        public void gameUpdated(Game g) {

            if (System.currentTimeMillis() > lastUpdate + updateRate) {
                fireTableRowsUpdated(0, getRowCount());
                lastUpdate = System.currentTimeMillis();
            }
        }

        public void playerJoined(Game game, Player player) {
            // nada
        }

        public void playerLeft(Game game, Player player) {
            // nada
        }

        public void gameEntitiesAdded(Game game) {
        }

        public void gameEntitiesRemoved(Game game) {
        }
    }

    @Override
    public boolean isCellEditable(int rowIndex, int columnIndex) {
        return columnOrder[columnIndex].isEditable();
    }

    public int getRowCount() {
        return type.size();
    }

    public int getColumnCount() {
        return columnOrder.length;
    }

    public Object getValueAt(int rowIndex, int columnIndex) {
        try {
            return columnOrder[columnIndex].getValue(
                    type.getId(rowIndex), data.get(rowIndex));
        } catch (java.lang.IndexOutOfBoundsException e) {
            // this is a concurrency issue but should self correct
            return null;
        }
    }

    @Override
    public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
        boolean updated = columnOrder[columnIndex].setValue(
                type.getId(rowIndex), data.get(rowIndex), aValue);
        if (updated) {
            fireTableCellUpdated(rowIndex, columnIndex);
        }
    }

    @Override
    public String getColumnName(int column) {
        return columnOrder[column].getName();
    }

    @Override
    public Class<?> getColumnClass(int columnIndex) {
        return columnOrder[columnIndex].getValueClass();
    }

    protected abstract static class Column {

        private String name;
        protected Class<?> c;

        public Column(Class<?> c, String name) {
            this.name = name;
            this.c = c;
        }

        public String getName() {
            return name;
        }

        public boolean isEditable() {
            return true;
        }

        public Class<?> getValueClass() {
            return c;
        }

        public abstract Object getValue(EntityType t, EntityData d);

        public abstract boolean setValue(EntityType t, EntityData d,
                Object value);
    }

    protected class IdColumn extends Column {

        public IdColumn() {
            super(Integer.class, "Id");
        }

        @Override
        public Object getValue(EntityType t, EntityData d) {
            return t.getId();
        }

        public boolean setValue(EntityType e, EntityData d, Object value) {
            throw new RuntimeException(new OperationNotSupportedException());
        }

        @Override
        public boolean isEditable() {
            return false;
        }
    }

    protected class NameColumn extends Column {

        public NameColumn() {
            super(String.class, "Name");
        }

        @Override
        public Object getValue(EntityType t, EntityData d) {
            return t.getName();
        }

        public boolean setValue(EntityType t, EntityData d, Object value) {
            throw new RuntimeException(new OperationNotSupportedException());
        }

        @Override
        public boolean isEditable() {
            return false;
        }
    }

    protected class MinEnergyColumn extends Column {

        public MinEnergyColumn() {
            super(Double.class, "Min Energy");
        }

        @Override
        public Object getValue(EntityType t, EntityData d) {
            return new Double(d.getMinEnergy());
        }

        public boolean setValue(EntityType t, EntityData d, Object value) {
            final double newEnergy = (Double) value;
            if (newEnergy != d.getMinEnergy()) {
                d.setMinEnergy(newEnergy);
                return true;
            } else {
                return false;
            }
        }
    }

    protected class MaxEnergyColumn extends Column {

        public MaxEnergyColumn() {
            super(Double.class, "Max Energy");
        }

        @Override
        public Object getValue(EntityType t, EntityData d) {
            return new Double(d.getMaxEnergy());
        }

        public boolean setValue(EntityType t, EntityData d, Object value) {
            final double newEnergy = (Double) value;
            if (newEnergy != d.getMaxEnergy()) {
                d.setMaxEnergy(newEnergy);
                return true;
            } else {
                return false;
            }
        }
    }

    protected class DieOnMinEnergyColumn extends Column {

        public DieOnMinEnergyColumn() {
            super(Boolean.class, "Die On Min Energy");
        }

        @Override
        public Object getValue(EntityType t, EntityData d) {
            return new Boolean(d.isDieOnMinEnergy());
        }

        public boolean setValue(EntityType t, EntityData d, Object value) {
            final boolean newValue = (Boolean) value;
            if (newValue != d.isDieOnMinEnergy()) {
                d.setDieOnMinEnergy(newValue);
                return true;
            } else {
                return false;
            }
        }
    }

    protected class EnergyDepletionRateColumn extends Column {

        public EnergyDepletionRateColumn() {
            super(Double.class, "Energy Depletion Rate");
        }

        @Override
        public Object getValue(EntityType t, EntityData d) {
            return new Double(d.getEnergyDepletionRate());
        }

        public boolean setValue(EntityType t, EntityData d, Object value) {
            final double newEnergy = (Double) value;
            if (newEnergy != d.getEnergyDepletionRate()) {
                d.setEnergyDepletionRate(newEnergy);
                return true;
            } else {
                return false;
            }
        }
    }

    protected class MassColumn extends Column {

        public MassColumn() {
            super(Double.class, "Mass");
        }

        @Override
        public Object getValue(EntityType t, EntityData d) {
            return new Double(d.getMass());
        }

        public boolean setValue(EntityType t, EntityData d, Object value) {
            final double newValue = (Double) value;
            if (newValue != d.getMass()) {
                d.setMass(newValue);
                return true;
            } else {
                return false;
            }
        }
    }

    protected class RadiusColumn extends Column {

        public RadiusColumn() {
            super(Double.class, "Radius");
        }

        @Override
        public Object getValue(EntityType t, EntityData d) {
            return new Double(d.getMaxRadius());
        }

        public boolean setValue(EntityType t, EntityData d, Object value) {
            throw new RuntimeException(new OperationNotSupportedException());
        }

        @Override
        public boolean isEditable() {
            return false;
        }


    }
    
}
